package com.zgy.mycmdline;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zgy.enums.OptionEnum;
import com.zgy.job.Job;
import com.zgy.job.Jobs;
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;


public class MycmdLineParser {
    private static final Logger LOGGER= LoggerFactory.getLogger(MycmdLineParser.class);
    private static final Options options = new Options();
    private static final OptionGroup optionGroup = new OptionGroup();
    private static final PosixParser posixParser = new PosixParser();
    private static final HelpFormatter helpFormatter = new HelpFormatter();
    private static final Job cmdJob = new Job();
    private static final Job.SheetConfig sheetConfig = new Job.SheetConfig();
    private static final ArrayList<Job.SheetConfig> arrayList= new ArrayList<>();
    private static final OptionEnum MODEL_F=OptionEnum.file;
    private static final OptionEnum MODEL_C=OptionEnum.cmd;
    private static final List<String> optionNames = Arrays.stream(OptionEnum.values()).map(OptionEnum::getLongOption).collect(Collectors.toList());
    private static final ExecutorService executorService = ThreadUtil.newExecutor(2,4);

    public static void parser(String[] cmdLineStr) throws ParseException {
        //创建Options
        createOptions();
        //解析命令行
        CommandLine commandLine = posixParser.parse(options, cmdLineStr);
        //设置Option默认值并执行
        setOptionsDefaultValueAndDoWrite(commandLine);
    }

    private static void setOptionsDefaultValueAndDoWrite(CommandLine commandLine) {
        if (commandLine.hasOption(OptionEnum.help.getShortOption())||commandLine.hasOption(OptionEnum.help.getLongOption())||commandLine.getOptions().length==0) printHelp();
        if (commandLine.hasOption(MODEL_C.getShortOption())||commandLine.hasOption(MODEL_C.getLongOption())) setOptionForModelC(commandLine);
        if (commandLine.hasOption(MODEL_F.getShortOption())||commandLine.hasOption(MODEL_F.getLongOption())) setOptionForModelF(commandLine);
    }

    private static void setOptionForModelF(CommandLine commandLine) {

        if (commandLine.hasOption(OptionEnum.fileEx.getLongOption())){
            System.out.println(OptionEnum.fileEx.getDefaultValue());
            printHelp();
        }

        if (commandLine.hasOption(OptionEnum.JsonFormat.getLongOption())){
            System.out.println(JSONUtil.parseObj(OptionEnum.JsonFormat.getDefaultValue()).toStringPretty());
            printHelp();
        }

        if(commandLine.hasOption(OptionEnum.JsonPath.getLongOption())){
            Charset charset;
            if (commandLine.hasOption(OptionEnum.JsonPathCharSet.getLongOption())){
                charset=Charset.forName(commandLine.getOptionValue(OptionEnum.JsonPathCharSet.getLongOption()));
            }else {
                charset=Charset.forName(OptionEnum.JsonPathCharSet.getDefaultValue());
            }
            JSONObject jsonPath = JSONUtil.readJSONObject(Paths.get(commandLine.getOptionValue(OptionEnum.JsonPath.getLongOption())).toFile(), charset);
            Jobs jobs = JSONUtil.toBean(jsonPath, Jobs.class);
            jobs.getJobs().forEach(executorService::execute);
        }else {
            System.err.println(OptionEnum.JsonPath.getLongOption()+":必填项");
            printHelp();
        }
    }

    private static void setOptionForModelC(CommandLine commandLine) {
        if (commandLine.hasOption(OptionEnum.cmdEx.getLongOption())){
            System.out.println(OptionEnum.cmdEx.getDefaultValue());
            printHelp();
        }

        if (commandLine.hasOption(OptionEnum.ModuleExcel.getLongOption())){
            int i = commandLine.getOptionValue(OptionEnum.ModuleExcel.getLongOption()).lastIndexOf(".");
            String excelExtentName = commandLine.getOptionValue(OptionEnum.ModuleExcel.getLongOption()).substring(i);
            if ("xls".equals(excelExtentName)){
                printHelp();
            }
            cmdJob.setModuleExcel(commandLine.getOptionValue(OptionEnum.ModuleExcel.getLongOption()));
        }else{
            cmdJob.setModuleExcel(null);
        }

        if (commandLine.hasOption(OptionEnum.InsertExcel.getLongOption())){
            cmdJob.setInsertExcel(commandLine.getOptionValue(OptionEnum.InsertExcel.getLongOption()));
        }else{
            System.err.println(OptionEnum.InsertExcel.getLongOption()+":必填项");
            printHelp();
        }

        if (commandLine.hasOption(OptionEnum.CommitRow.getLongOption())){
            cmdJob.setCommitRow(Integer.parseInt(commandLine.getOptionValue(OptionEnum.CommitRow.getLongOption())));
        }else{
            cmdJob.setCommitRow(Integer.parseInt(OptionEnum.CommitRow.getDefaultValue()));
        }

        if (commandLine.hasOption(OptionEnum.SheetName.getLongOption())){
            sheetConfig.setSheetName(commandLine.getOptionValue(OptionEnum.SheetName.getLongOption()));
        }else {
            sheetConfig.setSheetName(OptionEnum.SheetName.getDefaultValue());
        }

        if (commandLine.hasOption(OptionEnum.DataFileName.getLongOption())){
            sheetConfig.setDataFileName(commandLine.getOptionValue(OptionEnum.DataFileName.getLongOption()));
        }else {
            System.err.println(OptionEnum.DataFileName.getLongOption()+":必填项");
            printHelp();
        }

        if (commandLine.hasOption(OptionEnum.DataFileFieldSeparator.getLongOption())){
            sheetConfig.setDataFileFieldSeparator(commandLine.getOptionValue(OptionEnum.DataFileFieldSeparator.getLongOption()));
        }else {
            sheetConfig.setDataFileFieldSeparator(OptionEnum.DataFileFieldSeparator.getDefaultValue());
        }

        if (commandLine.hasOption(OptionEnum.DataFileCharSet.getLongOption())){
            sheetConfig.setDataFileCharSet(commandLine.getOptionValue(OptionEnum.DataFileCharSet.getLongOption()));
        }else {
            sheetConfig.setDataFileCharSet(OptionEnum.DataFileCharSet.getDefaultValue());
        }

        if (commandLine.hasOption(OptionEnum.ExcelStartRowNum.getLongOption())){
            sheetConfig.setExcelStartRowNum(Integer.parseInt(commandLine.getOptionValue(OptionEnum.ExcelStartRowNum.getLongOption())));
        }else {
            sheetConfig.setExcelStartRowNum(Integer.parseInt(OptionEnum.ExcelStartRowNum.getDefaultValue()));
        }

        if (commandLine.hasOption(OptionEnum.ExcelStartColNum.getLongOption())){
            sheetConfig.setExcelStartColNum(Integer.parseInt(commandLine.getOptionValue(OptionEnum.ExcelStartColNum.getLongOption())));
        }else {
            sheetConfig.setExcelStartColNum(Integer.parseInt(OptionEnum.ExcelStartColNum.getDefaultValue()));
        }

        if (commandLine.hasOption(OptionEnum.DataFileNumberTypeCols.getLongOption())){
            JSONArray dataFileNumberTypeCols = JSONUtil.parseArray(commandLine.getOptionValue(OptionEnum.DataFileNumberTypeCols.getLongOption()));
            List<Integer> list = dataFileNumberTypeCols.stream().map(o -> (Integer) o).collect(Collectors.toList());
            sheetConfig.setDataFileNumberTypeCols(list);
        }else {
            JSONArray dataFileNumberTypeCols = JSONUtil.parseArray(OptionEnum.DataFileNumberTypeCols.getDefaultValue());
            List<Integer> list = dataFileNumberTypeCols.stream().map(o -> (Integer) o).collect(Collectors.toList());
            sheetConfig.setDataFileNumberTypeCols(list);
        }

        //构建job任务
        buildJob();
        //校验job对象的正确性
        jobCheck(cmdJob);
        //执行job任务
        execJob(commandLine);
    }


    private static void execJob(CommandLine commandLine) {
        if (commandLine.hasOption(MODEL_C.getShortOption())) cmdJob.writeToExcel();
    }

    private static void buildJob() {
        arrayList.add(sheetConfig);
        cmdJob.setSheetConfigList(arrayList);
    }

    public static void jobCheck(Job job){

        boolean flag=false;
        int i = job.getModuleExcel().lastIndexOf(".");
        String excelExtentName = job.getModuleExcel().substring(i);
        if (".xls".equals(excelExtentName)){
            System.err.println((OptionEnum.ModuleExcel.getLongOption()+"：模版表格仅支持Excel*.xlsx版本的表格"));
            flag=true;
        }

        if (flag) printHelp();
    }

    private static void printHelp() {
        helpFormatter.setOptionComparator((o1, o2) -> {
            Option option1 = (Option) o1;
            Option option2 = (Option) o2;
            return optionNames.indexOf(option1.getLongOpt()) - optionNames.indexOf(option2.getLongOpt());
        });
        helpFormatter.printHelp(OptionEnum.help.getShortOption()+"|"+OptionEnum.help.getLongOption(),OptionEnum.help.getDefaultValue(),options,"");
        System.exit(0);
    }


    public static void createOptions() {

        Option Help = OptionBuilder
                .hasArg(false)
                .withLongOpt(OptionEnum.help.getLongOption())
                .withDescription(OptionEnum.help.getDescription())
                .create(OptionEnum.help.getShortOption());

        Option c = OptionBuilder.hasArg(false)
                .isRequired()
                .withLongOpt(OptionEnum.cmd.getLongOption())
                .withDescription(OptionEnum.cmd.getDescription())
                .create(OptionEnum.cmd.getShortOption());

        Option cmdEx = OptionBuilder.hasArg(false)
                .withLongOpt(OptionEnum.cmdEx.getLongOption())
                .withDescription(OptionEnum.cmdEx.getDescription())
                .create();

        Option ModuleExcel = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.ModuleExcel.getLongOption())
                .withArgName(OptionEnum.ModuleExcel.getArgs())
                .withDescription(OptionEnum.ModuleExcel.getDescription())
                .create();

        Option InsertExcel = OptionBuilder.hasArg()
                .withLongOpt(OptionEnum.InsertExcel.getLongOption())
                .withArgName(OptionEnum.InsertExcel.getArgs())
                .withDescription(OptionEnum.InsertExcel.getDescription())
                .create();

        Option CommitRow = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.CommitRow.getLongOption())
                .withArgName(OptionEnum.CommitRow.getArgs())
                .withDescription(OptionEnum.CommitRow.getDescription())
                .create();

        Option SheetName = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.SheetName.getLongOption())
                .withArgName(OptionEnum.SheetName.getArgs())
                .withDescription(OptionEnum.SheetName.getDescription())
                .create();

        Option DataFileName = OptionBuilder.hasArg()
                .withLongOpt(OptionEnum.DataFileName.getLongOption())
                .withArgName(OptionEnum.DataFileName.getArgs())
                .withDescription(OptionEnum.DataFileName.getDescription())
                .create();

        Option DataFileFieldSeparator = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.DataFileFieldSeparator.getLongOption())
                .withArgName(OptionEnum.DataFileFieldSeparator.getArgs())
                .withDescription(OptionEnum.DataFileFieldSeparator.getDescription())
                .create();

        Option DataFileCharSet = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.DataFileCharSet.getLongOption())
                .withArgName(OptionEnum.DataFileCharSet.getArgs())
                .withDescription(OptionEnum.DataFileCharSet.getDescription())
                .create();

        Option DataFileNumberTypeCols = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.DataFileNumberTypeCols.getLongOption())
                .withArgName(OptionEnum.DataFileNumberTypeCols.getArgs())
                .withDescription(OptionEnum.DataFileNumberTypeCols.getDescription())
                .create();

        Option ExcelStartRowNum = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.ExcelStartRowNum.getLongOption())
                .withArgName(OptionEnum.ExcelStartRowNum.getArgs())
                .withDescription(OptionEnum.ExcelStartRowNum.getDescription())
                .create();

        Option ExcelStartColNum = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.ExcelStartColNum.getLongOption())
                .withArgName(OptionEnum.ExcelStartColNum.getArgs())
                .withDescription(OptionEnum.ExcelStartColNum.getDescription())
                .create();

        Option f = OptionBuilder.hasArg(false)
                .isRequired()
                .withLongOpt(OptionEnum.file.getLongOption())
                .withDescription(OptionEnum.file.getDescription())
                .create(OptionEnum.file.getShortOption());

        Option fileEx = OptionBuilder.hasArg(false)
                .withLongOpt(OptionEnum.fileEx.getLongOption())
                .withDescription(OptionEnum.fileEx.getDescription())
                .create();

        Option JsonFormat = OptionBuilder.hasArg(false)
                .withLongOpt(OptionEnum.JsonFormat.getLongOption())
                .withDescription(OptionEnum.JsonFormat.getDescription())
                .create();

        Option JsonPath = OptionBuilder.hasArg()
                .withLongOpt(OptionEnum.JsonPath.getLongOption())
                .withArgName(OptionEnum.JsonPath.getArgs())
                .withDescription(OptionEnum.JsonPath.getDescription())
                .create();

        Option JsonPathCharSet = OptionBuilder.hasOptionalArg()
                .withLongOpt(OptionEnum.JsonPathCharSet.getLongOption())
                .withArgName(OptionEnum.JsonPathCharSet.getArgs())
                .withDescription(OptionEnum.JsonPathCharSet.getDescription())
                .create();

        optionGroup.addOption(c);
        optionGroup.addOption(f);
        options.addOptionGroup(optionGroup);
        options.addOption(Help);
        options.addOption(cmdEx);
        options.addOption(fileEx);
        options.addOption(JsonFormat);
        options.addOption(JsonPath);
        options.addOption(JsonPathCharSet);
        options.addOption(ModuleExcel);
        options.addOption(InsertExcel);
        options.addOption(CommitRow);
        options.addOption(SheetName);
        options.addOption(DataFileName);
        options.addOption(DataFileFieldSeparator);
        options.addOption(DataFileCharSet);
        options.addOption(DataFileNumberTypeCols);
        options.addOption(ExcelStartRowNum);
        options.addOption(ExcelStartColNum);
    }
}
